/********************************************************************
 *                                                                  *
 * THIS FILE IS PART OF THE OggWrit SOFTWARE CODEC SOURCE CODE.     *
 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
 *                                                                  *
 * THE OggWrit SOURCE CODE IS (C) COPYRIGHT 2003-2007               *
 * by the XIPHOPHORUS Company http://www.xiph.org/                  *
 *                                                                  *
 ********************************************************************

 encode.c: Writ stream encoding

 ********************************************************************/

#include <stdlib.h>
#include <string.h>
#include <writ/writ.h>

static void writ_text_write(oggpack_buffer *opb, writ_text *wt) {
  int i;
  oggpack_write(opb, wt->length, 8);
  for (i = 0; i < wt->length; ++i)
    oggpack_write(opb, wt->string[i], 8);
}

int writ_encode_init(writ_state *ws, ogg_uint32_t granule_num,
                     ogg_uint32_t granule_den) {
  ws->granulepos = -1;
  ws->num_phrases = 0;
  ws->skip_phrases = 0;
  ws->num_headers = 0;
  ws->opb = _ogg_malloc(sizeof(oggpack_buffer));
  oggpack_writeinit(ws->opb);
  ws->wi = _ogg_malloc(sizeof(writ_info));
  ws->wi->version = 0;
  ws->wi->subversion = 0;
  ws->wi->granulerate_numerator = granule_num;
  ws->wi->granulerate_denominator = granule_den;
  ws->wi->num_languages = 0;
  ws->wi->languages = 0;
  ws->wi->num_windows = 0;
  ws->wi->windows = 0;
  return 0;
}

int writ_encode_clear(writ_state *ws) {
  int i;
  for (i = 0; i < ws->wi->num_languages; ++i)
    _ogg_free(ws->wi->languages[i].language_name.string);
  _ogg_free(ws->wi->languages);
  _ogg_free(ws->wi->windows);
  _ogg_free(ws->wi);
  oggpack_writeclear(ws->opb);
  _ogg_free(ws->opb);
  return 0;
}

int writ_encode_lang_add(writ_state *ws, char *name, char *desc) {
  writ_language *new_lang;
  int name_length = (int)strlen(name);
  int desc_length = (int)strlen(desc);
  if (1 > ws->wi->subversion) ws->wi->subversion = 1;
  if (255 == ws->wi->num_languages) return -8;
  ws->wi->languages = _ogg_realloc(ws->wi->languages,
    (1 + ws->wi->num_languages) * sizeof(writ_language));
  new_lang = ws->wi->languages + ws->wi->num_languages;
  new_lang->language_name.length = name_length;
  new_lang->language_name.string =
    _ogg_malloc(name_length + desc_length);
  memcpy(new_lang->language_name.string, name, name_length); 
  new_lang->language_desc.length = desc_length;
  new_lang->language_desc.string =
    new_lang->language_name.string + name_length;
  memcpy(new_lang->language_desc.string, desc, desc_length); 
  return ++ws->wi->num_languages;
}

int writ_encode_wind_scale(writ_state *ws, int scale_x, int scale_y) {
  if (2 > ws->wi->subversion) ws->wi->subversion = 2;
  ws->wi->location_scale_x = scale_x;
  ws->wi->location_scale_y = scale_y;
  return 0;
}

int writ_encode_wind_add(writ_state *ws, int left, int top, int width,
                         int height, int align_x, int align_y) {
  writ_window *new_window;
  if (2 > ws->wi->subversion) ws->wi->subversion = 2; /* ??? */
  if (255 == ws->wi->num_windows) return -8;
  ws->wi->windows = _ogg_realloc(ws->wi->windows,
    (1 + ws->wi->num_windows) * sizeof(writ_window));
  new_window = ws->wi->windows + ws->wi->num_windows;
  new_window->location_x = left;
  new_window->location_y = top;
  new_window->location_width = width;
  new_window->location_height = height;
  new_window->alignment_x = align_x;
  new_window->alignment_y = align_y;
  return ++ws->wi->num_windows;
}

int writ_encode_header(writ_state *ws, ogg_packet *op) {
  int i, bitx, bity;

  if (0 < ws->num_phrases) return -1;
  if (ws->num_headers > ws->wi->subversion) return 1;
  oggpack_reset(ws->opb);
  oggpack_write(ws->opb, ws->num_headers, 8);
  oggpack_write(ws->opb, 'tirw', 32);
  op->b_o_s = 0;
  switch (ws->num_headers)
  {
  case 0:
    oggpack_write(ws->opb, ws->wi->version, 8);
    oggpack_write(ws->opb, ws->wi->subversion, 8);
    oggpack_write(ws->opb, ws->wi->granulerate_numerator, 32);
    oggpack_write(ws->opb, ws->wi->granulerate_denominator, 32);
    op->b_o_s = 1;
    break;
  case 1:
    oggpack_write(ws->opb, ws->wi->num_languages, 8);
    for (i = 0; i < ws->wi->num_languages; ++i) {
      writ_text_write(ws->opb, &ws->wi->languages[i].language_name);
      writ_text_write(ws->opb, &ws->wi->languages[i].language_desc);
    }
    break;
  case 2:
    oggpack_write(ws->opb, ws->wi->location_scale_x, 16);
    oggpack_write(ws->opb, ws->wi->location_scale_y, 16);
    oggpack_write(ws->opb, ws->wi->num_windows, 8);
    bitx = writ_ilog(ws->wi->location_scale_x);
    bity = writ_ilog(ws->wi->location_scale_y);
    for (i = 0; i < ws->wi->num_windows; ++i) {
      writ_window *wn = &ws->wi->windows[i];
      oggpack_write(ws->opb, wn->location_x, bitx);
      oggpack_write(ws->opb, wn->location_y, bity);
      oggpack_write(ws->opb, wn->location_width, bitx);
      oggpack_write(ws->opb, wn->location_height, bity);
      oggpack_write(ws->opb, wn->alignment_x, 2);
      oggpack_write(ws->opb, wn->alignment_y, 2);
    }
    break;
  }
  op->packet = oggpack_get_buffer(ws->opb);
  op->bytes = oggpack_bytes(ws->opb);
  op->e_o_s = 0;
  op->packetno = 0;
  op->granulepos = 0;
  return 0;
}

/*
  Should split this into writ_encode_phrasein and writ_encode_packetout?
*/
int writ_encode_phrase(writ_state *ws, ogg_packet *op,
                       ogg_int64_t start, ogg_int32_t duration,
                       char **text, int window_id) {
  int i;

  oggpack_reset(ws->opb);
  oggpack_write(ws->opb, 0xFF, 8);
  oggpack_write(ws->opb, (unsigned long)start, 32);
  oggpack_write(ws->opb, (unsigned long)(start >> 32), 32);
  oggpack_write(ws->opb, duration, 32);
  i = 0;
  do {
    writ_text wt;
    wt.length = (int)strlen(text[i]);
    wt.string = text[i];
    writ_text_write(ws->opb, &wt);
  } while (++i < ws->wi->num_languages);
  if (0 < ws->wi->num_windows)
    oggpack_write(ws->opb, window_id, 8);
  op->packet = oggpack_get_buffer(ws->opb);
  op->bytes = oggpack_bytes(ws->opb);
  op->b_o_s = 0;
  op->e_o_s = 0;
  op->packetno = ++ws->num_phrases;
  op->granulepos = start;
  return 0;
}

